package com.jijuxie.common.aspect;

import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import com.jijuxie.common.annotation.RateLimiter;
import com.jijuxie.common.exception.ServiceException;

/**
 * 限流处理
 */
@Aspect
@Component
public class RateLimiterAspect {
    private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);

    private static final ConcurrentHashMap<String, com.google.common.util.concurrent.RateLimiter> RATE_LIMITER_CACHE = new ConcurrentHashMap<>();

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Before("@annotation(rateLimiter)")
    public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable {
        int time = rateLimiter.time();
        int count = rateLimiter.count();
        String key = getKey(point);
        try {
            // 使用Redis进行分布式限流
            String redisKey = "rate_limit:" + key;
            // 原子操作递增并获取当前值
            Long increment = redisTemplate.opsForValue().increment(redisKey);
            
            // 第一次访问
            if (increment == 1) {
                // 设置过期时间
                redisTemplate.expire(redisKey, time, TimeUnit.SECONDS);
            }
            
            // 超过限制次数
            if (increment > count) {
                log.warn("接口({})访问过于频繁，限流处理", key);
                throw new ServiceException("请求过于频繁，请稍后重试");
            }
        } catch (Exception e) {
            // 如果Redis不可用，降级为本地限流
            com.google.common.util.concurrent.RateLimiter rateLimiterLocal = RATE_LIMITER_CACHE.get(key);
            if (rateLimiterLocal == null) {
                rateLimiterLocal = com.google.common.util.concurrent.RateLimiter.create(count / (double) time);
                RATE_LIMITER_CACHE.put(key, rateLimiterLocal);
            }
            
            if (!rateLimiterLocal.tryAcquire()) {
                log.warn("接口({})访问过于频繁，限流降级处理", key);
                throw new ServiceException("请求过于频繁，请稍后重试");
            }
        }
    }

    private String getKey(JoinPoint point) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        RateLimiter rateLimiter = AnnotationUtils.findAnnotation(method, RateLimiter.class);
        return rateLimiter.key().isEmpty() ? 
                method.getDeclaringClass().getName() + "." + method.getName() : 
                rateLimiter.key();
    }
} 